package com.xzh.spring;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class AnnotationAwareAspectJAutoProxyCreator implements BeanPostProcessor {
    /**
     * 用于存储通过 @Before 注解解析出的切入点及通知
     */
    private ConcurrentHashMap<String, List<MethodWithClass>> beforeMethodMap = new ConcurrentHashMap<>();

    /**
     * 用于存储通过 @After 注解解析出的切入点及通知
     */
    private ConcurrentHashMap<String, List<MethodWithClass>> afterMethodMap = new ConcurrentHashMap<>();


    public void setBeforeMethodMap(ConcurrentHashMap<String, List<MethodWithClass>> beforeMethodMap) {
        this.beforeMethodMap = beforeMethodMap;
    }

    public void setAfterMethodMap(ConcurrentHashMap<String, List<MethodWithClass>> afterMethodMap) {
        this.afterMethodMap = afterMethodMap;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        return bean;
    }

    /**
     * 想要完成 AOP 操作只需通过此方法对 bean 进行加工即可
     *
     * @param bean
     * @param beanName
     * @return
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        // 存储该 bean 的前置 methodName
        ArrayList<String> beforeMethodNameList = new ArrayList<>();
        // 存储该 bean 的后置 methodName
        ArrayList<String> afterMethodNameList = new ArrayList<>();
        fillMethodNameList(beanName, beforeMethodNameList, beforeMethodMap);

        fillMethodNameList(beanName, afterMethodNameList, afterMethodMap);

        // 判断是否需要创建代理对象
        if (!beforeMethodNameList.isEmpty() || !afterMethodNameList.isEmpty()) {
            // 创建代理对象
            Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    // 判断当前方法是否在beforeMethodNameList中
                    if (beforeMethodNameList.contains(method.getName())) {
                        // 组合 得到 beanName.method  格式的key
                        String key = beanName + "." + method.getName();
                        // 判单beforeMethodMap 中是否含有对应key
                        if (beforeMethodMap.containsKey(key)) {
                            List<MethodWithClass> classList = beforeMethodMap.get(key);
                            // 依次调用这些方法
                            for (MethodWithClass methodWithClass : classList) {
                                Object instance = methodWithClass.getClazz().getDeclaredConstructor().newInstance();
                                methodWithClass.getMethod().invoke(instance);
                            }
                        }
                    }

                    // 执行原本的方法
                    Object invoke = method.invoke(bean, args);

                    // 判断当前方法是否在afterMethodNameList中
                    if (afterMethodNameList.contains(method.getName())) {
                        // 组合 得到 beanName.method  格式的key
                        String key = beanName + "." + method.getName();
                        // 判单beforeMethodMap 中是否含有对应key
                        if (afterMethodMap.containsKey(key)) {
                            List<MethodWithClass> classList = afterMethodMap.get(key);
                            // 依次调用这些方法
                            for (MethodWithClass methodWithClass : classList) {
                                Object instance = methodWithClass.getClazz().getDeclaredConstructor().newInstance();
                                methodWithClass.getMethod().invoke(instance);
                            }
                        }
                    }


                    // 返回原本的值
                    return invoke;
                }
            });
            // 直接返回代理对象来代替 bean 对象
            return proxy;

        }
        return bean;
    }

    private void fillMethodNameList(String beanName, ArrayList<String> methodNameList, ConcurrentHashMap<String, List<MethodWithClass>> methodMap) {
        for (String key : methodMap.keySet()) {
            // 把 Map 中的 x.x 格式拆分成 beanName 和 methodName
            String[] split = key.split("\\."); //   \\. 转义符
            // 判断数组中的 beanName 是否与传入的 beanName 相同
            if (split[0].equals(beanName)) {
                methodNameList.add(split[1]);
            }
        }
    }
}
